package org.example.physics_engine.geometry;

import lombok.ToString;
import org.example.physics_engine.Epsilon;

@ToString
public class Vector2d {

    //x坐标
    public double x;
    //y坐标
    public double y;

    //构造函数
    public Vector2d(double x, double y) {
        this.x = x;
        this.y = y;
    }
    public Vector2d() { this(0, 0); }
    public Vector2d(Vector2d vector2d) {
        this.x = vector2d.x;
        this.y = vector2d.y;
    }

    //判断两个向量相等
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        Vector2d v = (Vector2d) obj;
        return Math.abs(this.x - v.x) <= Epsilon.E && Math.abs(this.y - v.y) <= Epsilon.E;

    }

    //复制一个和本向量相同的向量
    public Vector2d copy() {
        return new Vector2d(this.x, this.y);
    }

    //计算本点与给定点之间的距离的平方
    public double distanceSquared(Vector2d point) {
        double dx = this.x - point.x;
        double dy = this.y - point.y;
        return dx * dx + dy * dy;
    }

    //返回本向量的模
    public double getMagnitude() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    //返回模的平方
    public double getMagnitudeSquared() {
        return this.x * this.x + this.y * this.y;
    }

    //加一个向量
    public Vector2d add(Vector2d vector) {
        this.x += vector.x;
        this.y += vector.y;
        return this;
    }

    //向量相加，返回新的和向量
    public Vector2d sum(Vector2d vector) {
        return new Vector2d(this.x + vector.x, this.y + vector.y);
    }

    //减一个向量
    public Vector2d sub(Vector2d vector) {
        this.x -= vector.x;
        this.y -= vector.y;
        return this;
    }

    //向量相减，返回新的差向量
    public Vector2d difference(Vector2d vector) {
        return new Vector2d(this.x - vector.x, this.y - vector.y);
    }

    //创建一个从本点指向参数点的向量，实际上也是向量相减
    public Vector2d to(Vector2d vector) {
        return new Vector2d(vector.x - this.x, vector.y - this.y);
    }

    //向量数乘
    public Vector2d multiplyNum(double scalar) {
        this.x *= scalar;
        this.y *= scalar;
        return this;
    }

    //向量数除
    public Vector2d divideNum(double scalar) {
        this.x /= scalar;
        this.y /= scalar;
        return this;
    }

    //向量数乘，返回数乘后的新向量
    public Vector2d product(double scalar) {
        return new Vector2d(this.x * scalar, this.y * scalar);
    }

    //向量点乘
    public double dotProduct(Vector2d vector) {
        return this.x * vector.x + this.y * vector.y;
    }

    //向量叉乘
    public double crossProduct(Vector2d vector) {
        return this.x * vector.y - this.y * vector.x;
    }

    //计算本向量和z轴上长度为传入参数的向量的叉乘
    public Vector2d crossZ(double z) {
        return new Vector2d(-this.y * z, this.x * z);
    }

    //判断本向量是否为0向量
    public boolean isZero() {
        return Math.abs(this.x) <= Epsilon.E && Math.abs(this.y) <= Epsilon.E;
    }

    //本向量取反
    public Vector2d negate() {
        this.x = -this.x;
        this.y = -this.y;
        return this;
    }

    //返回一个新的本向量取反的向量
    public Vector2d getNegative() {
        return new Vector2d(-x, -y);
    }

    //将此向量设置为该向量的左手法线
    public Vector2d left() {
        double temp = this.x;
        this.x = this.y;
        this.y = -temp;
        return this;
    }

    //将此向量设置为该向量的右手法线
    public Vector2d right() {
        left();
        return negate();
    }

    //将本向量置为零向量
    public Vector2d zero() {
        x = 0.0;
        y = 0.0;
        return this;
    }

    //规范化：返回此向量的单位向量
    public Vector2d getNormalized() {
        double magnitude = this.getMagnitude();
        if (magnitude <= Epsilon.E) return new Vector2d();
        magnitude = 1.0 / magnitude;
        return new Vector2d(this.x * magnitude, this.y * magnitude);
    }

    //规范化本向量并返回未规范化前的模
    public double normalize() {
        double magnitude = Math.sqrt(this.x * this.x + this.y * this.y);
        if (magnitude <= Epsilon.E) return 0;
        double m = 1.0 / magnitude;
        this.x *= m;
        this.y *= m;
        return magnitude;
    }

    //将本向量投影到给定向量上，返回新向量
    public Vector2d project(Vector2d vector) {
        //点乘
        double dotProd = this.dotProduct(vector);
        double denominator = vector.dotProduct(vector);
        //零向量
        if (denominator <= Epsilon.E) return new Vector2d();
        denominator = dotProd / denominator;
        return new Vector2d(denominator * vector.x, denominator * vector.y);
    }
}
